!-----------------------------------------------------------------------
!BOP
! !IROUTINE: p_d_adjust --- complete full physics update
!
! !INTERFACE: 
  subroutine p_d_adjust(grid, tracer, pelnxy, pkxy, pkzxy, zvir, &
                        cappa, delpxy, ptxy, pexy, psxy, ptop)

! !USES:
    use shr_kind_mod, only: r8 => shr_kind_r8
    use dynamics_vars, only : T_FVDYCORE_GRID
#if defined( SPMD )
    use parutilitiesmodule, only: parcollective, sumop
#endif
    use repro_sum_mod, only : repro_sum, repro_sum_tol_exceeded, &
                              repro_sum_rel_diff_max, &
                              repro_sum_recompute
    use cam_logfile,   only : iulog
    use perf_mod
!-----------------------------------------------------------------------
    implicit none

! !INPUT PARAMETERS:
    type (T_FVDYCORE_GRID), intent(in) :: grid
    real(r8), intent(in) :: zvir
    real(r8), intent(in) :: ptop
    real(r8), intent(in) :: cappa

! !INPUT/OUTPUT PARAMETERS:
    real(r8), intent(inout) :: tracer(grid%ifirstxy:grid%ilastxy,                     &
                                      grid%jfirstxy:grid%jlastxy,grid%km,grid%ntotq) ! constituents
    real(r8), intent(inout) :: delpxy(grid%ifirstxy:grid%ilastxy,                     &
                                      grid%jfirstxy:grid%jlastxy,grid%km) ! pressure difference
    real(r8), intent(inout) :: ptxy (grid%ifirstxy:grid%ilastxy,                      &
                                     grid%jfirstxy:grid%jlastxy, grid%km) ! Virtual pot temp
    real(r8), intent(inout) :: pexy(grid%ifirstxy:grid%ilastxy,                       &
                                    grid%km+1,grid%jfirstxy:grid%jlastxy)

! !OUTPUT PARAMETERS
    real(r8), intent(out) :: psxy(grid%ifirstxy:grid%ilastxy, grid%jfirstxy:grid%jlastxy)  ! surf. press
    real(r8), intent(out) :: pelnxy(grid%ifirstxy:grid%ilastxy,grid%km+1,grid%jfirstxy:grid%jlastxy) ! interface pres
    real(r8), intent(out) :: pkzxy(grid%ifirstxy:grid%ilastxy,grid%jfirstxy:grid%jlastxy, grid%km)    ! Layer-mean value of PK
    real(r8), intent(out) :: pkxy(grid%ifirstxy:grid%ilastxy,grid%jfirstxy:grid%jlastxy, grid%km+1)  ! PE**cappa

! !DESCRIPTION:
!
!   Complete adjustment of quantities after physics update
!
! !REVISION HISTORY:
!   00.06.01   Grant?     Creation
!   01.06.08   AAM        Created from p_d_coupling
!   02.04.24   WS         New mod_comm interface
!   02.05.01   WS         Fix of S.-J. and Phil to peln, pk update
!   03.03.31   BAB        dry mass adjustment moved to dme_adjust, just finish up here
!   05.07.06   WS         Use grid argument to get all grid-related data
!   05.09.23   WS         Transitioned to XY variables only
!   06.07.01   WS         Transitioned tracers q3 to T_TRACERS
!   08.06.25   PW         Added call to fixed point reproducible sum
!
!EOP
!-----------------------------------------------------------------------
!BOC
! !LOCAL VARIABLES:
    real(r8), parameter ::  D0_0                    =  0.0_r8
    real(r8), parameter ::  D1_0                    =  1.0_r8

    real(r8) :: pole(grid%ifirstxy:grid%ilastxy,grid%km,grid%ntotq+2)
                                               !  Array containing local pole values
    real(r8) :: pole_sum(grid%km,grid%ntotq+2) !  Array containing average of all pole values
    real(r8) :: rel_diff(2,grid%km,grid%ntotq+2)
    real(r8),allocatable :: pole_tmp(:)

    integer :: i, k, m, j ! indices
    integer :: im, jm, km, ntotq, lim
    integer :: ifirstxy, ilastxy, jfirstxy, jlastxy

    logical  :: write_warning

!---------------------------End Local workspace-------------------------

!
! ----------------------------------------------------
! Complete update of dynamics variables
! ----------------------------------------------------
!

    im       = grid%im
    jm       = grid%jm
    km       = grid%km
    ntotq    = grid%ntotq

    ifirstxy = grid%ifirstxy
    ilastxy  = grid%ilastxy
    jfirstxy = grid%jfirstxy
    jlastxy  = grid%jlastxy

    lim      = (ilastxy-ifirstxy) + 1

    ! Average the pole values (WS 2006/02/16, bug fix)

    if (jfirstxy==1) then

       !$omp parallel do private(i,k,m)
       do k = 1, km
          do i = ifirstxy, ilastxy
             pole(i,k,1) = delpxy(i,1,k)
             pole(i,k,2) =   ptxy(i,1,k)
          enddo
          do m = 1, ntotq
             do i = ifirstxy, ilastxy
                pole(i,k,m+2) = tracer(i,1,k,m)
             enddo
          enddo
       enddo

       call t_startf("pdadj_repro_sum")
       call repro_sum(pole, pole_sum, lim, lim, km*(ntotq+2), gbl_count=im, &
                      commid=grid%commxy_x, rel_diff=rel_diff) ! South pole
       call t_stopf("pdadj_repro_sum")

       ! check that "fast" reproducible sum is accurate enough. If not, calculate
       ! using old method
       write_warning = .false.
       if (grid%myidxy_x == 0) write_warning = .true.
       if ( repro_sum_tol_exceeded('p_d_adjust/South Pole', km*(ntotq+2), &
                                   write_warning, iulog, rel_diff) ) then
          if ( repro_sum_recompute ) then
             call t_startf("pdadj_sumfix")
             allocate( pole_tmp(im) )
             do m = 1, ntotq+2
                do k = 1, km
                   if (rel_diff(1,k,m) > repro_sum_rel_diff_max) then
                      pole_tmp(:) = D0_0
                      do i = ifirstxy, ilastxy
                         pole_tmp(i) = pole(i,k,m)
                      enddo
#if defined(SPMD)
                      call parcollective(grid%commxy_x,sumop,im,pole_tmp)
#endif
                      pole_sum(k,m) = D0_0
                      do i = 1, im
                         pole_sum(k,m) = pole_sum(k,m) + pole_tmp(i)
                      enddo
                   endif
                enddo
             enddo
             deallocate( pole_tmp )
             call t_stopf("pdadj_sumfix")
          endif
       endif

       ! save results
       !$omp parallel do private(i,k,m)
       do k = 1, km
          ! normalize first
          do m = 1, ntotq+2
             pole_sum(k,m) = pole_sum(k,m)/im
          enddo
          do i = ifirstxy,ilastxy
             delpxy(i,1,k) = pole_sum(k,1)
             ptxy(i,1,k)   = pole_sum(k,2)
          enddo
          do m = 1, ntotq
             do i = ifirstxy,ilastxy
                tracer(i,1,k,m) = pole_sum(k,m+2)
             enddo
          enddo
       enddo

    endif ! jfirstxy==1

    if (jlastxy==jm) then

       !$omp parallel do private(i,k,m)
       do k = 1, km
          do i = ifirstxy, ilastxy
             pole(i,k,1) = delpxy(i,jm,k)
             pole(i,k,2) =   ptxy(i,jm,k)
          enddo
          do m = 1, ntotq
             do i = ifirstxy, ilastxy
                pole(i,k,m+2) = tracer(i,jm,k,m)
             enddo
          enddo
       enddo

       call t_startf("pdadj_repro_sum")
       call repro_sum(pole, pole_sum, lim, lim, km*(ntotq+2), gbl_count=im, &
                      commid=grid%commxy_x, rel_diff=rel_diff) ! North pole
       call t_stopf("pdadj_repro_sum")

       ! check that "fast" reproducible sum is accurate enough. If not, calculate
       ! using old method
       write_warning = .false.
       if (grid%myidxy_x == 0) write_warning = .true.
       if ( repro_sum_tol_exceeded('p_d_adjust/Nouth Pole', km*(ntotq+2), &
                                   write_warning, iulog, rel_diff) ) then
          if ( repro_sum_recompute ) then
             call t_startf("pdadj_sumfix")
             allocate( pole_tmp(im) )
             do m = 1, ntotq+2
                do k = 1, km
                   if (rel_diff(1,k,m) > repro_sum_rel_diff_max) then
                      pole_tmp(:) = D0_0
                      do i = ifirstxy, ilastxy
                         pole_tmp(i) = pole(i,k,m)
                      enddo
#if defined(SPMD)
                      call parcollective(grid%commxy_x,sumop,im,pole_tmp)
#endif
                      pole_sum(k,m) = D0_0
                      do i = 1, im
                         pole_sum(k,m) = pole_sum(k,m) + pole_tmp(i)
                      enddo
                   endif
                enddo
             enddo
             deallocate( pole_tmp )
             call t_stopf("pdadj_sumfix")
          endif
       endif

       ! save results
       !$omp parallel do private(i,k,m)
       do k = 1, km
          ! normalize first
          do m = 1, ntotq+2
             pole_sum(k,m) = pole_sum(k,m)/im
          enddo
          do i = ifirstxy,ilastxy
             delpxy(i,jm,k) = pole_sum(k,1)
             ptxy(i,jm,k)   = pole_sum(k,2)
          enddo
          do m = 1, ntotq
             do i = ifirstxy,ilastxy
                tracer(i,jm,k,m) = pole_sum(k,m+2)
             enddo
          enddo
       enddo

    endif ! jlastxy==jm

    !
    ! Fix pe,ps if nontrivial z decomposition
    ! Transpose pe - change to better method (16-byte?) later on
    !

    !
    ! Compute pexy
    !
    !$omp parallel do private(i, j)
    do j = jfirstxy,jlastxy
       do i = ifirstxy, ilastxy
          pexy(i,1,j) = ptop
       enddo
    enddo

    !$omp parallel do private(i, j, k)
    do j = jfirstxy,jlastxy
       do k = 1, km
          do i = ifirstxy, ilastxy
             pexy(i,k+1,j) = pexy(i,k,j) + delpxy(i,j,k)
          enddo
       enddo
    enddo

    do j=jfirstxy,jlastxy
       do i=ifirstxy,ilastxy
          psxy(i,j) = pexy(i,km+1,j)
       enddo
    enddo

    !$omp parallel do private(i, j, k)
    do j=jfirstxy,jlastxy
       
       !
       ! Update pelnxy and pkxy
       !
       do k = 1, km+1
          do i = ifirstxy, ilastxy
             pelnxy(i,k,j) = log( pexy(i,k,j) )
             pkxy  (i,j,k) = pexy(i,k,j) ** cappa
          enddo
       enddo

       !
       ! Update pkzxy
       !
       do k = 1,km
          do i = ifirstxy,ilastxy
             pkzxy(i,j,k) = (pkxy(i,j,k+1)-pkxy(i,j,k))/(cappa*(pelnxy(i,k+1,j)-pelnxy(i,k,j)))
          enddo
       enddo
    enddo     ! jfirstxy:jlastxy loop

    !
    ! Calculate virtual potential temperature
    !

    !$omp parallel do private(i, j, k)
    do j=jfirstxy,jlastxy
       do k = 1,km
          do i = ifirstxy,ilastxy
             ptxy(i,j,k) = ptxy(i,j,k)*                  &
                (D1_0+zvir*tracer(i,j,k,1)) &
                /pkzxy(i,j,k)
          enddo
       enddo
    enddo     ! jfirstxy:jlastxy loop

    !EOC
 end subroutine p_d_adjust
!-----------------------------------------------------------------------
